/**
 * Copyright 2008-2009 HAN.BIFANG
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package han.bashe.common.converter;

import han.bashe.common.exception.BasheErrorCode;
import han.bashe.common.exception.BasheException;
import han.bashe.common.message.NormalizedMessage;
import han.bashe.common.message.RequestHelper;
import han.bashe.common.util.ExceptionUtils;

import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.QName;

import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlString;
import org.w3.x2003.x05.soapEnvelope.EnvelopeDocument;
import org.w3.x2003.x05.soapEnvelope.FaultDocument;
import org.w3.x2003.x05.soapEnvelope.Header;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import v1.message.bashe.han.BasheSoapHeadersDocument;
import v1.message.bashe.han.HeadDocument.Head;

/**
 * @Author Kent
 */
public class Soap12Converter {
	private static String xq = "declare namespace xq='urn:han.bashe.message.v1'"
			+ "declare namespace soap='http://www.w3.org/2003/05/soap-envelope'";
	private static String namespace = "http://www.w3.org/2003/05/soap-envelope";

	private static String FAULT = "Fault";

	public static NormalizedMessage toNormalized(String xml, boolean webservices)
			throws Exception {

		EnvelopeDocument edoc = EnvelopeDocument.Factory.parse(xml);
		if (!edoc.validate()) {
			throw new BasheException(BasheErrorCode.SOAP_MESSAGE_FORMAT_ERROR,
					"request xml is not valid soap message");
		}
		NormalizedMessage msg = new NormalizedMessage();
		Header h = edoc.getEnvelope().getHeader();
		XmlObject[] xs = h.selectPath(xq + "$this/xq:BasheSoapHeaders");
		if (xs.length > 0) {
			BasheSoapHeadersDocument doc = BasheSoapHeadersDocument.Factory
					.parse(xs[0].newDomNode());
			if (!doc.validate()) {
				throw new BasheException(
						BasheErrorCode.SOAP_MESSAGE_FORMAT_ERROR,
						"BasheSoapHeaders format is invalid");
			}
			List<Head> hs = doc.getBasheSoapHeaders().getHeadList();
			for (Head head : hs) {
				msg.setHead(head.getName(), head.getValue());
			}
		} else {
			throw new BasheException(
					BasheErrorCode.SOAP_MESSAGE_FORMAT_ERROR,
					"missing required soap header element 'BasheSoapHeaders@urn:han.bashe.message.v1'");

		}

		NodeList nl = edoc.getEnvelope().getBody().newDomNode().getChildNodes();
		Node tmp = null;
		for (int i = 0; i < nl.getLength(); i++) {
			if (nl.item(i).getLocalName() != null) {
				// && nl.item(i).getLocalName().equals("service")) {
				tmp = nl.item(i);
				break;
			}
		}
		nl = tmp.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			if (nl.item(i).getLocalName() != null) {
				tmp = nl.item(i);
				break;
			}
		}
		XmlObject bodyDoc = XmlObject.Factory.parse(tmp);
		msg.setBody(bodyDoc.toString());

		if (webservices) {
			RequestHelper rh = new RequestHelper(msg);
			rh.setWebServicesInputParam(tmp.getLocalName() + "@"
					+ tmp.getNamespaceURI());
		}
		return msg;
	}

	public static String toSoap(NormalizedMessage msg) {

		StringBuffer sb = new StringBuffer();

		sb
				.append("<SOAP-ENV:Envelope xmlns:bs='urn:han.bashe.message.v1' xmlns:SOAP-ENV='http://www.w3.org/2003/05/soap-envelope'>");
		sb.append("<SOAP-ENV:Header>");
		sb.append("<bs:BasheSoapHeaders>");
		for (String name : msg.getHeads().keySet()) {
			sb.append("<bs:Head>");
			sb.append("<bs:name>" + name + "</bs:name>");
			sb.append("<bs:value>" + msg.getHead(name) + "</bs:value>");
			sb.append("</bs:Head>");
		}
		sb.append("</bs:BasheSoapHeaders>");

		sb.append("</SOAP-ENV:Header>");
		sb.append("<SOAP-ENV:Body>");
		
		RequestHelper rh = new RequestHelper(msg);
		String input = null;
		String ns = null;
		if (rh.getWebServicesInputParam() != null) {
			StringTokenizer st = new StringTokenizer(rh
					.getWebServicesInputParam(), "@");
			input = st.nextToken();
			ns = st.nextToken();
			sb.append("<" + input + " xmlns='" + ns + "'>");
		}
		sb.append((String) msg.getBody());
		if (rh.getWebServicesInputParam() != null) {
			sb.append("</" + input + ">");
		}
		
		sb.append("</SOAP-ENV:Body>");
		sb.append("</SOAP-ENV:Envelope>");
		return sb.toString();
	}

	// public static String toSoapFault(BasheException e){
	// StringBuffer sb = new StringBuffer();
	//
	// sb
	// .append("<SOAP-ENV:Envelope
	// xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'
	// xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'
	// xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
	// xmlns:xsd='http://www.w3.org/2001/XMLSchema'>");
	//	
	// }

	public static String toSoapFault(BasheException e) {
		FaultDocument doc = FaultDocument.Factory.newInstance();
		QName q = null;
		if (ExceptionUtils.isClientFault(e.getCode())) {
			q = new QName(namespace, "Sender");
		} else {
			q = new QName(namespace, "Receiver");
		}
		doc.addNewFault().addNewCode().setValue(q);
		doc.getFault().addNewReason().addNewText().setStringValue(
				e.getCode() + " " + e.getMsg());
		XmlString s = XmlString.Factory.newInstance();
		s.setStringValue(ExceptionUtils.getTraceAsString(e));
		doc.getFault().addNewDetail().set(s);

		StringBuffer sb = new StringBuffer();

		sb
				.append("<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' >");
		sb.append("<SOAP-ENV:Body>");
		sb.append(doc.toString());
		sb.append("</SOAP-ENV:Body>");
		sb.append("</SOAP-ENV:Envelope>");
		return sb.toString();
	}

	public static String toSoapFault(Exception e) {
		FaultDocument doc = FaultDocument.Factory.newInstance();
		QName q = new QName(namespace, "Receiver");

		doc.addNewFault().addNewCode().setValue(q);
		doc.getFault().addNewReason().addNewText().setStringValue(
				BasheErrorCode.UNKNOWN_ERROR + " " + e.getMessage());
		XmlString s = XmlString.Factory.newInstance();
		s.setStringValue(ExceptionUtils.getTraceAsString(e));
		doc.getFault().addNewDetail().set(s);

		StringBuffer sb = new StringBuffer();

		sb
				.append("<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' >");
		sb.append("<SOAP-ENV:Body>");
		sb.append(doc.toString());
		sb.append("</SOAP-ENV:Body>");
		sb.append("</SOAP-ENV:Envelope>");
		return sb.toString();
	}

	public static NormalizedMessage toNormalizedRes(String xml)
			throws Exception {

		EnvelopeDocument edoc = EnvelopeDocument.Factory.parse(xml);
		if (!edoc.validate()) {
			throw new BasheException(
					BasheErrorCode.SERVICE_RESPONSE_XML_FORMAT_ERROR,
					"response xml is not valid soap message");
		}
		NormalizedMessage msg = new NormalizedMessage();
		Header h = edoc.getEnvelope().getHeader();

		// when process soap service response, the header may be null
		if (h != null) {
			XmlObject[] xs = h.selectPath(xq
					+ "$this/soap:Header/xq:BasheSoapHeaders");
			if (xs.length > 0) {
				BasheSoapHeadersDocument doc = BasheSoapHeadersDocument.Factory
						.parse(xs[0].newDomNode());
				if (!doc.validate()) {
					throw new BasheException(
							BasheErrorCode.SERVICE_RESPONSE_XML_FORMAT_ERROR,
							"BasheSoapHeaders format is invalid");
				}
				List<Head> hs = doc.getBasheSoapHeaders().getHeadList();
				for (Head head : hs) {
					msg.setHead(head.getName(), head.getValue());
				}
			}
		}
		NodeList nl = edoc.getEnvelope().getBody().newDomNode().getChildNodes();
		Node tmp = null;
		for (int i = 0; i < nl.getLength(); i++) {
			if (nl.item(i).getLocalName() != null) {
				// && nl.item(i).getLocalName().equals("service")) {
				tmp = nl.item(i);
				break;
			}
		}
		nl = tmp.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			if (nl.item(i).getLocalName() != null) {
				tmp = nl.item(i);
				break;
			}
		}
		if (tmp.getLocalName().equals(FAULT)
				&& tmp.getNamespaceURI().equals(namespace)) {
			throw new BasheException(
					BasheErrorCode.SERVICE_RETURN_SOAP_FAULT_ERROR, xml);
		}
		XmlObject bodyDoc = XmlObject.Factory.parse(tmp);
		msg.setBody(bodyDoc.toString());

		return msg;
	}

}
